
package com.x52im.rbchat.logic.main;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Html;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.TextView;

import com.eva.android.AppManager;
import com.eva.android.widget.Action;
import com.eva.android.widget.ActivityRoot;
import com.eva.android.widget.WidgetUtils;
import com.eva.android.widget.alert.AlertDialog;
import com.eva.android.x.XToolKits;
import com.eva.framework.dto.LoginInfo2;
import com.eva.framework.dto.LogoutInfo;
import com.x52im.rbchat.IMClientManager;
import com.x52im.rbchat.MyApplication;
import com.x52im.rbchat.R;
import com.x52im.rainbowchat.http.logic.dto.RosterElementEntity;
import com.x52im.rbchat.utils.NotificationPromptHelper;
import com.x52im.rbchat.logic.main.loginimpl.LoginHelper;
import com.x52im.rbchat.logic.main.loginimpl.LoginInfoToSave;
import com.x52im.rbchat.network.http.HttpRestHelper;
import com.x52im.rbchat.utils.IntentFactory;
import com.x52im.rbchat.utils.PreferencesToolkits;

import net.x52im.mobileimsdk.android.ClientCoreSDK;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Observer;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

/**
 * 登陆Activity实现类.
 * 
 * <p>
 * <b>重要说明：</b>现在登陆认证是在版本检查时通过连接业务服务器来完成的（且测试阶段只验证用户正确性而不验让密码）。
 * <br>
 * RainbowChat的“登陆”其实包含两层含义：
 * 1）登陆HTTP服务器进行用户的身份验证（通过登陆框输入的用户名和密码）；
 * 2）第1)步成功后再连接到IM服务器（使用第1)步中得到的uid和token）。
 * <br>
 * 换句话说，RainbowChat登陆时的身份认证是完全可以也应该由独立的SSO单点登陆接口来提供，而这也是MobileIMSDK v3建议
 * 的最佳实践。
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @version 1.3
 */
public class LoginActivity extends ActivityRoot
{
	// 请求码：前往注册页面
	public final static int REQUEST_CODE_FOR_REGISTER = 3;

	public final static String TAG = LoginActivity.class.getSimpleName();
	
	/** 文本编辑组件：登陆用户名/email */
	private EditText txtUid = null;
	/** 文本编辑组件：登陆密码 */
	private EditText txtLoginPsw = null;
	/** CheckBox组件：显示密码内容 */
	private CheckBox cbShowPass = null;
	/** 按钮：登陆 */
	private Button btnSubmit = null;
	/** 按钮：退出 */
	//private Button btnExit = null;
	/** 按钮：注册 */
	private Button btnRegister = null;
	/** 按钮：忘记了密码？ */
	private Button btnForgot = null;
	/** 背景图 */
	private View loginBg;
	
	/** 
	 * Called when the activity is first created.
	 */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		// 全面屏沉浸式效果（系统顶部状态栏将变的透明，界面从状态栏下方透过）
		XToolKits.setStatusBarTranslucent(this, true);
		// 全面屏沉浸式效果（系统底部导航栏将变的透明，界面从导航栏下方透过）
		XToolKits.setNavigationBarTranslucent(this, true);
		super.onCreate(savedInstanceState);
	}

	/**
	 * 重写父类方法，取消深色状态栏文字的设置。
	 */
	@Override
	protected void configStatusBarTextColorDark()
	{
		// 什么也不需要做
	}
	
	/**
	 * 本方法由父类中的 {@link #onCreate(Bundle)} 方法调用，重写本方法可以实现额外的逻辑。
	 */
	@Override
	protected void init(Bundle savedInstanceState)
	{
		super.init(savedInstanceState);

//		// 初始化ui组件
//		initViews();
//		// 为ui组件增加事件监听
//		initListeners();
		// 初始化自动登陆逻辑
		initAutoLogin();
	}

	/**
	 * 本方法由父类的 {@link #init(Bundle)} 调用，子类可在此方法中实现ui的初始化。
	 */
	@Override
	protected void initViews(Bundle savedInstanceState)
	{
		//养成良好习惯：首先设置主layout，确保后绪的操作中使用到的组件都可以被find到
		setContentView(R.layout.login_form_v9);
		
		txtUid = this.findViewById(R.id.login_form_uidEdit);
		txtLoginPsw = this.findViewById(R.id.login_form_passwordEdit);
		cbShowPass = this.findViewById(R.id.login_form_shoiwPassCheckBox);
		btnSubmit = this.findViewById(R.id.login_form_submigBtn);
		btnRegister = this.findViewById(R.id.login_form_registerBtn);
		btnForgot = this.findViewById(R.id.login_form_forgotPswBtn);
		loginBg = this.findViewById(R.id.login_form_bg);

		// 在程序里设置文本是为了显示下划线
//		btnForgot.setText(Html.fromHtml("<u>"+this.getString(R.string.login_form_btn_forgetpsw)+"</u>"));

		//当前版本文本显示
		((TextView)this.findViewById(R.id.login_form_versionView))
			.setText(MessageFormat.format($$(R.string.login_form_version_info), LoginHelper.getAPKVersionName(), LoginHelper.getAPKVersionCode()));

		//开启自定义标题栏后，在AndroidManifest.xml设定android:label标题栏上
		//将不会起效，必须在代码中显示调用setTitle哦，具体见ActivityRoot中的说明
		this.setTitle(getText(R.string.app_name2));

		// 背景图微动画效果
		startBgAnimation();
	}

	/**
	 * 本方法由父类的 {@link #init(Bundle)} 调用，子类可在此方法中为各UI功能组件增加事件临听。
	 */
	@Override
	protected void initListeners()
	{
		// 登陆按钮事件处理
		btnSubmit.setOnClickListener(v -> doLogin());
		// 注册按钮事件处理
		btnRegister.setOnClickListener(v -> ActivityCompat.startActivityForResult(self(), IntentFactory.createRegisterIntent(self()), REQUEST_CODE_FOR_REGISTER, null));
		// 忘记按钮密码事件处理
		btnForgot.setOnClickListener(v -> startActivity(IntentFactory.createForgetPassWordIntent(self())));
		// 显示密码内容CheckBox事件处理（如果选中则显示密码内容，否则不显示）
		cbShowPass.setOnCheckedChangeListener((buttonView, isChecked) -> {
			// 设置密码内容是否可见
			txtLoginPsw.setTransformationMethod(isChecked?HideReturnsTransformationMethod.getInstance():PasswordTransformationMethod.getInstance());
			// 光标移动到尾部
			txtLoginPsw.setSelection(txtLoginPsw.length());
		});
	}

	/**
	 * 初始化自动登陆逻辑。
	 */
	protected void initAutoLogin()
	{
		// 初始化默认登陆用户名，之前登陆成功时会自动把最新登陆用户名记下来的，所以此处可以还原最近用户名
		if(txtUid.getText().length() <= 0) {
			LoginInfoToSave lastLoginInfo = PreferencesToolkits.getDefaultLoginName(this);
			if(lastLoginInfo != null) {
				txtUid.setText(lastLoginInfo.getLoginName());
				txtLoginPsw.setText(lastLoginInfo.getLoginPsw());
				if(txtLoginPsw.length() > 0) {
					txtLoginPsw.requestFocus();
					((EditText)txtLoginPsw).setSelection(txtLoginPsw.length());
				}
			}
		}
	}
	
	/**
	 * 实施登陆的方法.
	 *
	 * 本方法中将判断登陆用名和密码的字面合法性（比如不能为空等），然后执行真正的异步登陆处理.
	 *
	 * @see LoginHelper
	 */
	private void doLogin()
	{
		if(String.valueOf(txtUid.getText()).trim().length()<=0){
			txtUid.setError($$(R.string.login_form_validate_login_name_empty));
			return;
		}
		if(txtLoginPsw.getText().length()<=0){
			txtLoginPsw.setError($$(R.string.login_form_validate_login_psw_length_less_six));
			return;
		}

		String uidOrMail = String.valueOf(this.txtUid.getText()).trim().toLowerCase();
		String loginPsw = String.valueOf(this.txtLoginPsw.getText()).trim();
		
		// 登陆/连接全部完成后要做的事
		Observer afterLoginIMServerSucessObs = (o, arg) -> LoginHelper.afterLoginIMServerSucess(self());

		// 提交用户登陆信息认证和版本检查等（这就是im产品中的完整登陆流程）
		LoginInfo2 li = LoginHelper.constructLoginInfo(this, uidOrMail, loginPsw);
		new LoginHelper.Login$CheckUpdateAsyc(this, li, null, afterLoginIMServerSucessObs).execute();
	}

	/**
	 * 背景微动画效果。
	 */
	private void startBgAnimation()
	{
		Animation animation = AnimationUtils.loadAnimation(self(), R.anim.login_bg_translate_anim);
		loginBg.startAnimation(animation);
	}

	//android2.0前需要重写onKeyDown方法才能实现，2.0及以后直接重写onBackPressed即可哦
	/**
	 * 捕获back键，实现调用 {@link #doLogout(Activity, boolean, Observer)}方法.
	 */
	@Override
	public void onBackPressed()
	{
		LoginActivity.doLogout(self(), true, null);
	}

	/**
	 * 处理回调.
	 * {@inheritDoc}
	 */
	public void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		super.onActivityResult(requestCode, resultCode, data);

		switch (requestCode) {
			case REQUEST_CODE_FOR_REGISTER:
				// 将注册成功后的uid和密码默认填到界面上，方便用户登陆
				if (resultCode == Activity.RESULT_OK) {
					//解析从intent中传过来的数据
					//目前用于注册成功后，自动把登陆名和密码传入登陆界面从而方便登陆的场景.
					ArrayList intentDatas = IntentFactory.parseLoginFormIntent(data);
					txtUid.setText((String) intentDatas.get(0));
					txtLoginPsw.setText((String) intentDatas.get(1));
				}
				break;
		}
	}
	
	//--------------------------------------------------------------------- 静态方法 START
	/**
	 * 退出APP的登陆，并释放所有资源。
	 * <p>
	 * 本方法会弹出一个确认对话框。
	 * 
	 * @param activity
	 * @param dontExitJVM false表示完成退出APP（可用于完关闭应用时），否则表
	 * 示仅退出所有Activity（可用于切换到登陆界面重新登陆等场景下）
	 * @param obsAfterLogoutIMServer 本观察者将在logout im server完成后，被
	 * 调用，当前此参数已知的是在更多界面中的”退出登陆“时使用了。一般来说，本参数
	 * 的设置只在exitJVM参数=false时才有意义，请参见源码
	 * 
	 * @see #doLogout(Activity, Action, boolean, Observer)
	 */
	public static void doLogout(final Activity activity, boolean dontExitJVM, final Observer obsAfterLogoutIMServer)
	{
		doLogout(activity, null, dontExitJVM, obsAfterLogoutIMServer);
	}
	
	/**
	 * 退出APP的登陆，并释放所有资源。
	 * <p>
	 * 本方法会弹出一个选择对话框，选是才真正实施退出，否则将调用参数
	 * cancelAction指明的要执行的动作.
	 * <p>
	 * 
	 * @param activity
	 * @param cancelAction 点击确认对话框“取消”时要额外完成的动作，本参数可
	 * 为null（意味着不需要处理额外动作）
	 * @param exitJVM true表示完成退出APP（可用于完关闭应用时），否则表
	 * 示仅退出所有Activity（可用于切换到登陆界面重新登陆等场景下）
	 * @param obsAfterLogoutIMServer 本观察者将在logout im server完成后，被
	 * 调用，当前此参数已知的是在更多界面中的”退出登陆“时使用了。一般来说，本参数
	 * 的设置只在exitJVM参数=false时才有意义，请参见源码
	 */
	public static void doLogout(final Activity activity, final Action cancelAction, final boolean exitJVM, final Observer obsAfterLogoutIMServer)
	{
		new AlertDialog.Builder(activity)
			.setTitle(activity.getResources().getString(R.string.general_are_u_sure))
			.setMessage(activity.getResources().getString(R.string.login_form_exit_app_tip))
			.setPositiveButton(activity.getResources().getString(R.string.general_ok), (dialog, which) -> doLogoutNoConfirm(activity, exitJVM, obsAfterLogoutIMServer))
			.setNegativeButton(activity.getResources().getString(R.string.general_cancel), null)
			.show(); 
	}
	
	/**
	 * 退出APP的登陆，并释放所有资源。
	 * <p>
	 * 注：方法中不涉及任何GUI，用在多线程中调用也没事。
	 * 
	 * @param context
	 * @param exitJVM true表示完成退出APP（可用于完关闭应用时），否则表
	 * 示仅退出所有Activity（可用于切换到登陆界面重新登陆等场景下）
	 * @param obsAfterLogoutIMServer 本观察者将在logout im server完成后，被
	 * 调用，当前此参数已知的是在更多界面中的”退出登陆“时使用了。一般来说，本参数
	 * 的设置只在exitJVM参数=false时才有意义，请参见源码
	 */
	public static void doLogoutNoConfirm(final Context context, final boolean exitJVM, final Observer obsAfterLogoutIMServer)
	{
		// TODO 退出登陆时如有其它事情要处理，可在此补充！

        //** 【1】注销HTTP服务器的登陆
		// 多端互踢被踢时isConnectedToServer()==false，此判断主要是为了过滤掉此种情况下的注销http登陆逻辑，别无它用
		if(ClientCoreSDK.getInstance().isConnectedToServer()) {
			new AsyncTask<Object, Integer, Object>() {
				@Override
				protected Object doInBackground(Object... objects) {
					RosterElementEntity localUserInfo = MyApplication.getInstance2().getIMClientManager().getLocalUserInfo();
					if (localUserInfo != null) {
						LogoutInfo ao = new LogoutInfo();
						ao.setUid(localUserInfo.getUser_uid());
						ao.setDeviceInfo("just in android!"); // 您可以填入更为详细的信息提交到服务端哦
						ao.setOsType(LoginInfo2.OS_TYPE_ANDRIOD);

						// 注销登陆无需理会返回值，只要提交就行了
						HttpRestHelper.submitLogoutToServer(ao);
					}
					return null;
				}
			}.execute();
		}


		//** 【2】注销IM服务器连接并释放IM框架所占的所有资源
		// 为何要使用观察者？因退出登陆数据发送是在异步线程中实现，使用观察者是确保退出JVM等是在数据已发送完成后再行的哦！
		Observer obsAfterDoLogoutIMServer = (observable, data) -> {
			// 释放APP应用占用的资源
			systemExit(context, exitJVM);
			// 通知观察者
			if (obsAfterLogoutIMServer != null)
				obsAfterLogoutIMServer.update(null, null);
		};
		// 通信正常的情况下，需要发出socket注销指令，并释放资源、通知观察者
		if(ClientCoreSDK.getInstance().isConnectedToServer()) {
			IMClientManager.doLogoutIMServer(context, obsAfterDoLogoutIMServer);
		}
		//## Bug Fix START: 20220523 by Jack Jiang（解决了客户端被踢后，再次登陆时提示错误码203的问题）
		// 通信断开的情况下，直接释放资源、通知观察者
		else{
			// 释放IM所占资源
			try {
				MyApplication.getInstance2().getIMClientManager().releaseMobileIMSDK();
			} catch (Exception e) {
				Log.e(TAG, "错误发生于logoutMobileIMSDK:!isConnectedToServer:releaseMobileIMSDK()时："+e.getMessage(), e);
			}

			// 通知观察者
			obsAfterDoLogoutIMServer.update(null, null);
		}
		//## Bug Fix END


		//** 【3】重置大文件下载管理器
        MyApplication.getInstance2().getIMClientManager().getBigFileDownloadManager().clear();
	}
	
	/**
	 * 退出应用并释放应用的所有资源（IM框架相关资源的释放并不在本方法内处理
	 * ，请见{@link IMClientManager#doLogoutIMServer(Context, Observer)}）。
	 * <p>
	 * 注：方法中不涉及任何GUI，用在多线程中调用也没事。
	 * 
	 * @param context
	 * @param exitJVM true表示完成退出APP（可用于完关闭应用时），否则表
	 * 示仅退出所有Activity（可用于切换到登陆界面重新登陆等场景下）
	 */
	public static void systemExit(Context context, boolean exitJVM)
	{
		// 清除APP产生的所有Notification
		NotificationPromptHelper.cancalAllNotification(context);
		
		// 直接退出APP
		if(exitJVM) {
			// 完成退出APP（退出JVM）
			AppManager.getAppManager().AppExit(context);
		}
		// 只是回收所有的activity（比如用于切换账号重新登陆的功能中）
		else {
			try {
				// 释放栈中的所有activity
				AppManager.getAppManager().finishAllActivity();
			} 
			catch (Exception e) {
				Log.w(TAG, e.getMessage(), e);
			}
		}
	}
	//--------------------------------------------------------------------- 静态方法 END

}
